package ezXpns.GUI;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.*;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Date;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.UUID;
import java.util.Vector;

import javax.swing.*;
import javax.swing.border.BevelBorder;

import com.toedter.calendar.JDateChooser;

import ezXpns.GUI.controllers.BudgetChangeController;
import ezXpns.GUI.controllers.RequestResult;
import net.miginfocom.swing.MigLayout;
import java.awt.Font;


/**
 * @author A0086931X
 */

//@author A0086931X
public class BudgetChangePanel extends JPanel {
	/**
	 * Create the panel.
	 */
	public BudgetChangePanel() {
		setLayout(new MigLayout("", "[645:645,grow,fill]", "[40:n,fill][300px:300px,grow,fill][30:n,baseline]"));

		pnlOptions = new JLayeredPane();
		add(pnlOptions, "cell 0 0,growx,aligny top");
		pnlOptions.setPreferredSize(new Dimension(300, 50));

		JPanel expenseToolBar = new JPanel();
		expenseToolBar.setBounds(6, 1, 630, 45);
		pnlOptions.setLayer(expenseToolBar, 1);
		pnlOptions.add(expenseToolBar);
		expenseToolBar.setLayout(new MigLayout("", "[75.00px,fill][75.00px,fill][377.00px,fill][73.00px,fill]",
				"[28px]"));

		// Button: undo
		btnUndo = new JButton("undo");
		btnUndo.setEnabled(false);
		expenseToolBar.add(btnUndo, "cell 0 0,alignx left,aligny top");
		btnUndo.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				onUndo();
			}
		});

		// Button: redo
		btnRedo = new JButton("redo");
		btnRedo.setEnabled(false);
		expenseToolBar.add(btnRedo, "cell 1 0,alignx left,aligny top");
		btnRedo.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				onRedo();
			}
		});

		// Date Chooser
		JDateChooser expenseDateChooser = new JDateChooser();
		expenseDateChooser.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		expenseToolBar.add(expenseDateChooser, "cell 2 0,alignx left,aligny top");
		expenseDateChooser.setDateFormatString("dd MMM yyyy");
		expenseDateChooser.setDate(new Date()); // displays current date by
												// default
		// customization for month and year
		// add listener for date change here.
		// after date change, run Check for items for the date selected.
		
		expenseDateChooser.addPropertyChangeListener(new PropertyChangeListener() {
			@Override
			public void propertyChange(PropertyChangeEvent e) {
				onDateChanged(e);
			}
		});

		// Button: search??duplicate?
		JButton expenseBtnSearch = new JButton("search");
		expenseToolBar.add(expenseBtnSearch, "cell 3 0,alignx left,aligny top");
		expenseBtnSearch.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				advancedSearchDialog.setBounds(e.getXOnScreen() + 80, e.getYOnScreen() - 30, 251, 300);
				showSearch();
			}
		});

		pnlSearch = new JPanel();
		pnlSearch.setBounds(6, 1, 630, 45);
		pnlSearch.setBorder(new BevelBorder(BevelBorder.RAISED, null, null, null, null));
		pnlSearch.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
		pnlOptions.setLayer(pnlSearch, 0);
		pnlOptions.add(pnlSearch);

		// Field: search
		txtSearchQuery = new JTextField();
		txtSearchQuery.addFocusListener(new FocusAdapter() {
			@Override
			public void focusGained(FocusEvent arg0) {
				txtSearchQuery.setForeground(Color.BLACK);
				if (txtSearchQuery.getText().equals("search query")) {
					txtSearchQuery.setText("");
				}
			}

			@Override
			public void focusLost(FocusEvent e) {
				if (txtSearchQuery.getText().equals("")) {
					txtSearchQuery.setForeground(Color.LIGHT_GRAY);
					txtSearchQuery.setText("search query");
				}
			}
		});
		txtSearchQuery.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				if (e.getKeyCode() == KeyEvent.VK_ENTER) {
					doSearch();
				}
			}
		});
		txtSearchQuery.setForeground(Color.LIGHT_GRAY);
		txtSearchQuery.setText("search query");
		pnlSearch.add(txtSearchQuery);
		txtSearchQuery.setColumns(40);

		JButton expenseSearchButton = new JButton("search");
		expenseSearchButton.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent arg0) {
				doSearch();
			}
		});
		pnlSearch.add(expenseSearchButton);

		JButton expenseCloseSearchBtn = new JButton("close");
		expenseCloseSearchBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				hideSearch();
			}
		});
		pnlSearch.add(expenseCloseSearchBtn);

		// Panel: list of entries
		pnlEntryList = new JPanel();
		pnlEntryList.setLayout(new MigLayout("", "[630px,grow,fill]", "[]"));

		expense_scroll_pane = new JScrollPane(pnlEntryList);

		add(expense_scroll_pane, "cell 0 1,aligny top,grow");
		expense_scroll_pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

		JPanel expenseDetailsPanel = new JPanel();
		add(expenseDetailsPanel, "cell 0 2,grow");
		expenseDetailsPanel.setLayout(new MigLayout("", "[136px][100px,grow,fill][182px][50px,grow][103px]", "[16px]"));
		lblSubtotal.setFont(new Font("SansSerif", Font.BOLD, 20));

		lblSubtotal.setHorizontalAlignment(SwingConstants.LEFT);
		expenseDetailsPanel.add(lblSubtotal, "cell 0 0,alignx left,aligny top");
		expenseDetailsPanel.add(lblNumberOfItems, "cell 2 0,alignx right,aligny bottom");

		JLabel lblClickToEditTypes = new JLabel("Click to Edit Types");
		lblClickToEditTypes.addMouseListener(new MouseAdapter() {

			@Override
			public void mouseClicked(MouseEvent e) {
				changeTypePanel.setBounds(e.getXOnScreen(), e.getYOnScreen() - 211, 245, 211);
				//changeTypePanel.setModal(true);
				changeTypePanel.setVisible(true);
			}
		});
		lblClickToEditTypes.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		expenseDetailsPanel.add(lblClickToEditTypes, "cell 4 0,alignx right,aligny bottom");

	}

	public void connect(BudgetChangeController controller) {
		if (this.controller != null)
			return;
		placeHolderEntry = createEmptyItem();
		placeHolderEntry.setDeletable(false);

		today = new Date();

		placeHolderEntry.setDate(today);
		changeTypePanel.connect(controller);

		pnlEntryList.add(placeHolderEntry, "wrap");

		this.controller = controller;
		advancedSearchDialog = new AdvancedSearchDialog(controller);
		controller.connect(this);
		controller.changeDate(today);// display today by default

	}

	public void setNewFocus() { // give the placeholder entry focus for user
		placeHolderEntry.setNewFocus();
		scrollToBottom();
	}

	// Controller functions set available payment/recurring types
	public void setAvailableTypes(Iterable<String> types) {
		ArrayList<String> allTypes = new ArrayList<String>();
		for (String type : types) {
			allTypes.add(type);
		}

		String[] allTypesArray = allTypes.toArray(new String[allTypes.size()]);
		changeTypePanel.setAvailableTypes(allTypesArray);
		placeHolderEntry.setAvailableTypes(allTypesArray);
		for (BudgetItem item : entryList) {
			item.setAvailableTypes(allTypesArray);
		}
		advancedSearchDialog.setAvailableTypes(allTypesArray);
		this.types = allTypesArray;
	}

	// begin to display a new batch of data ^param editable whether this is an
	// editable view
	public void beginView(boolean addable) {
		this.addable = addable;
		creatingView = true;
		clearList();
		placeHolderEntry.setVisible(true);
	}

	// end of a new batch of data
	public void endView() {
		creatingView = false;
		refreshView();
		scrollToBottom();
		placeHolderEntry.setVisible(addable);
	}

	// Add a new item to the current view. Can be called out of a
	// beginView/endView pair
	public void addItem(UUID id, String name, float amount, String type, String tags, Date date) {
		BudgetItem item = createEmptyItem();
		item.setId(id);
		item.setItemName(name);
		item.setAmount(Float.toString(amount));
		item.setType(type);
		item.setTags(tags);
		item.setDate(date);
		entryList.add(item);

		if (!creatingView)
			refreshView();

		updateCounters();
		validate();

		if (!creatingView)
			scrollToBottom();

	}

	// Edit an existing item in the current view. Can be called out of a
	// beginView/endView pair
	public void editItem(UUID id, String name, float amount, String type, String tags, Date date) {
		for (BudgetItem item : entryList) {
			if (item.getId().equals(id)) {
				item.setItemName(name);
				item.setAmount(Float.toString(amount));
				item.setDate(date);
				item.setType(type);
				item.setTags(tags);
				break;
			}
		}

		if (!creatingView)
			updateCounters();
	}

	// Remove an existing item from the current view. Can be called out of a
	// beginView/endView pair
	public void deleteItem(UUID id) {
		for (BudgetItem item : entryList) {
			if (item.getId().equals(id)) {
				entryList.remove(item);
				pnlEntryList.remove(item);
				break;
			}
		}

		if (!creatingView)
			refreshView();
	}

	public void setSubTotal(Float value) {
		lblSubtotal.setText("Subtotal : " + String.format("%.2f", value)); 
	}

	public void updateUndoRedoStatus() {
		boolean canUndo = controller.canUndo();
		boolean canRedo = controller.canRedo();

		btnUndo.setEnabled(canUndo);
		btnRedo.setEnabled(canRedo);

		if (canUndo)
			btnUndo.setToolTipText("Undo " + controller.getNextUndoActionName());
		if (canRedo)
			btnRedo.setToolTipText("Redo " + controller.getNextRedoActionName());
	}

	private void refreshView() {
		sortList();
		redrawList();
		updateCounters();
		pnlEntryList.validate();
	}

	private void sortList() {
		Collections.sort(entryList, new Comparator<BudgetItem>() {
			@Override
			public int compare(BudgetItem lhs, BudgetItem rhs) {
				return lhs.getId().compareTo(rhs.getId());
			}
		});
	}

	private void redrawList() {
		for (int i = 0; i < entryList.size(); ++i) {
			pnlEntryList.add(entryList.get(i), "wrap");
		}
		pnlEntryList.add(placeHolderEntry, "wrap");
	}

	private BudgetItem createEmptyItem() {
		BudgetItem item = new BudgetItem();
		if (types != null)
			item.setAvailableTypes(types);
		item.addDeleteHandler(DELETE_HANDLER);
		item.addSaveHandler(SAVE_HANDLER);
		return item;
	}

	private void clearList() {
		for (BudgetItem entry : entryList) {
			pnlEntryList.remove(entry);
		}
		entryList.clear();
	}

	private void updateCounters() {
		lblNumberOfItems.setText("Number of items: " + entryList.size());
		for (int numUpdater = 0; numUpdater < entryList.size(); numUpdater++) {
			entryList.get(numUpdater).setIndex(numUpdater + 1);
		}
	}

	private void scrollToBottom() {
		expense_scroll_pane.getVerticalScrollBar().setValue(entryList.size() * 30);
	}

	private void onUndo() {
		controller.undo();
		updateUndoRedoStatus();
	}

	private void onRedo() {
		controller.redo();
		updateUndoRedoStatus();
	}

	private void onDateChanged(PropertyChangeEvent e) {
		if ("date".equals(e.getPropertyName())) {
			if (!(today.equals((Date) e.getNewValue()))) {
				controller.changeDate((Date) e.getNewValue());
				today = (Date) e.getNewValue();
			}
		}
	}

	private void doSearch() {
		if (txtSearchQuery.getText() != "search query" && txtSearchQuery.getText() != "") {
			RequestResult result = controller.requestSearch(
					txtSearchQuery.getText(), null, null, null, null, null, null
			);
			if (!result.isAccepted()) {
				JOptionPane.showMessageDialog(null, result.getError());
			}
		}
	}

	private void showSearch() {
		pnlOptions.setLayer(pnlOptions.getComponent(1), 2);
		advancedSearchDialog.setVisible(true);
		txtSearchQuery.requestFocus();
	}

	private void hideSearch() {
		pnlOptions.setLayer(pnlOptions.getComponent(0), 0);
		advancedSearchDialog.setVisible(false);
		controller.closeSearch();
	}

	private final EventHandler<BudgetItem> DELETE_HANDLER = new EventHandler<BudgetItem>() {
		@Override
		public void handle(BudgetItem item) {
			controller.requestDeleteItem(item.getId());
			updateUndoRedoStatus();
		}
	};

	private final EventHandler<BudgetItem> SAVE_HANDLER = new EventHandler<BudgetItem>() {
		@Override
		public void handle(BudgetItem item) {
			if (item != placeHolderEntry) {
				RequestResult result = controller.requestEditItem(item.getId(), item.getItemName(), item.getAmount(),
						item.getType(), item.getTags(), item.getDate());

				if (!result.isAccepted()) {
					System.out.println(result.getError());
				} else {
					updateUndoRedoStatus();
				}

			} else {
				RequestResult result = controller.requestAddItem(item.getItemName(), item.getAmount(), item.getType(),
						item.getTags());

				if (!result.isAccepted()) {
					System.out.println(result.getError());
				} else {
					updateUndoRedoStatus();
					item.clearFields();
					item.setNewFocus();
				}
			}
		}
	};

	private static final long serialVersionUID = 1L;

	private BudgetChangeController controller;
	private Vector<BudgetItem> entryList = new Vector<BudgetItem>();
	private BudgetItem placeHolderEntry;
	private JPanel pnlEntryList;
	private JLabel lblSubtotal = new JLabel("Subtotal : " + "0");
	private JLabel lblNumberOfItems = new JLabel("Number of items: " + "0");
	private JLayeredPane pnlOptions;
	private JPanel pnlSearch;
	private JTextField txtSearchQuery;
	private JButton btnRedo;
	private JButton btnUndo;
	private JScrollPane expense_scroll_pane;
	private boolean creatingView = false;
	private String[] types;
	private ChangeTypeGUI changeTypePanel = new ChangeTypeGUI(this);
	private Date today;
	private boolean addable;
	private AdvancedSearchDialog advancedSearchDialog;
}
